import json
import os
import re
import sys
from json import JSONDecodeError
from typing import Dict, Any, List

import yaml

from lib import file_utils

##################################################################################################
# 配置文件名
config_filename = '.hornet.yml'
second_config_filename = '.hornetrc'
global_config: Dict[str, Any] or None = None
version_file = '.hornet.version.latest'


def get_config(path: str, filename: str = None) -> dict or None:
    """
    读取名为.hornet-up.json的配置文件
    :param filename: 指定文件名称
    :param path 配置文件所在目录
    :return: dict or None
    """
    global global_config
    if global_config is not None:
        return global_config
    file = filename or config_filename
    config_path = os.path.join(path, file)

    if not os.path.exists(config_path):
        file = second_config_filename
        config_path = os.path.join(path, file)
        if not os.path.exists(config_path):
            print('没有找到配置文件！ [%s]' % path)
            exit(0)
        return None

    if not os.path.isfile(config_path):
        print('[%s] 不是一个文件！' % file)
        exit(0)
        return None

    with open(config_path, encoding='utf-8') as config_file:
        content = config_file.read()

    global_config = yaml.full_load(content) if config_path.endswith('.yml') else json.loads(content)
    return global_config


def get_servers(config: dict = None) -> List[Dict] or None:
    """
    获取服务器配置列表，如果在调用过{get_config}方法之后,调用本方法可不需要入参
    :param config: 全配置信息
    :return: 服务器配置列表
    """
    config = config or global_config
    if isinstance(config, dict):
        servers = config.get('servers')
        if isinstance(servers, dict):
            server_list = []
            local_servers = get_local_server_map()
            for key in list(servers.keys()):
                it: dict = servers.get(key)
                it['name'] = key
                if 'server_key' in it:
                    server_key = it.get('server_key')
                    if server_key not in local_servers:
                        print('服务器%s不存在' % server_key)
                        exit()
                    server = local_servers.get(server_key)
                    it['host'] = server.get('host')
                    it['port'] = server.get('port')
                    it['username'] = server.get('username')
                    it['password'] = server.get('password')
                server_list.append(it)
            return server_list
        else:
            return servers
    return []


server_list_file = os.path.join(os.path.dirname(__file__), '../.caches/server-list.json')
server_map: Dict[str, Dict[str, str or int or bool]] or None = None


def get_local_server_map() -> Dict[str, Dict[str, str or int or bool]]:
    global server_map
    if server_map is not None:
        return server_map
    if not os.path.exists(server_list_file):
        return dict()
    try:
        server_map = json.loads(file_utils.read_file(server_list_file, 'utf-8'))
        return server_map
    except JSONDecodeError:
        print('文件格式错误')
        return dict()


def get_local_server_list() -> List[Dict[str, str or int or bool]]:
    """
    获取'.caches/server-list.json'中配置服务器信息
    """
    if not os.path.exists(server_list_file):
        return []
    server_list = []
    try:
        servers: dict = json.loads(file_utils.read_file(server_list_file, 'utf-8'))
        for key in list(servers.keys()):
            server: dict = servers.get(key)
            server['name'] = key
            server_list.append(server)
        return server_list
    except JSONDecodeError:
        print('文件格式错误')
        return server_list


allowed_actions = [
    'init',
    'up',
    'upgrade'
    'post',
    'clean',
    'clean-all',
    'exec',
    'connect',
    'bak',
    'restart-server',
    'server',
    'help'
]
args_map = {
    '-s': '--server',
    '-c': '--cmd',
    '-v': '--version',
    '-a': '--all',
    '-f': '--file',
    '-U': '--only-up',
    '-uv': '--upgrade-version',
    '-nv': '--not-use-version-file'
}
parsed_args: Dict[str, str] or None = None


def is_arg_option(arg: str) -> bool:
    return arg.startswith('-')


def parse_cmd_args():
    """
    将命令行参数转为字典对象，只识别参数名开头有两杠--符号的
    :return:  dict
    """
    global parsed_args
    if parsed_args is not None:
        return parsed_args
    if len(sys.argv) < 2:
        print('hornet: 错误的命令, 查看帮助<hornet help>')
        sys.exit(0)

    action: str = sys.argv[1]
    if action not in allowed_actions:
        print('hornet: 未知的操作')
        sys.exit(0)

    args = {
        '--action': action
    }

    from_idx = 2
    arg_len = len(sys.argv) - from_idx
    for idx, arg in enumerate(sys.argv[from_idx:]):
        if arg.startswith('--'):
            cmd = arg
            # print('cmd => ', cmd)

        elif arg.startswith('-'):
            if arg not in args_map:
                print('未知选项: ', arg)
                sys.exit(0)
            cmd = args_map.get(arg)

        else:
            # value item
            continue

        if idx < arg_len:
            next_item = sys.argv[from_idx + idx + 1]
            if is_arg_option(next_item):
                args[cmd] = ''
            else:
                args[cmd] = next_item
    parsed_args = args
    return parsed_args


def init_config(local_basepath: str):
    config_file = os.path.join(local_basepath, config_filename)
    if os.path.exists(config_file):
        print('配置文件已存在: %s' % config_file)
        return
    try:
        with open(config_file, 'w', encoding='utf-8') as file:
            template_file = os.path.join(os.path.dirname(__file__), '../.template.hornet.yml')
            file.write(file_utils.read_file(template_file, 'utf-8'))
            print('✅ 生成配置文件: %s' % config_file)
    except BaseException as e:
        print(str(e))


def get_latest_version_code(basepath: str) -> str or None:
    project_version_file = os.path.join(basepath, version_file)
    if not os.path.exists(project_version_file):
        # print('版本号文件[%s]不存在' % version_file)
        return None

    return file_utils.read_file(project_version_file, 'utf-8')


_path_sep_pattern = re.compile(r'[/\\]+')


def join_linux_path(apath: str, *paths: str) -> str:
    joined_path = os.path.join(apath, *paths)
    return re.sub(_path_sep_pattern, '/', joined_path)
